package com.tlgen.orm.factory;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.tlgen.orm.common.PageResult;
import com.tlgen.orm.model.MiddleParams;
import com.tlgen.orm.model.RelationParams;
import com.tlgen.orm.utils.ConnectUtils;
import com.tlgen.orm.utils.ORMUtils;
import com.tlgen.orm.utils.TraceLog;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.util.CollectionUtils;

import java.sql.Statement;
import java.util.List;
import java.util.Objects;

import static com.tlgen.orm.constant.SQLScript.*;
import static com.tlgen.orm.enums.Anno.ASSOCIATION;
import static com.tlgen.orm.utils.ORMUtils.*;

public class ModelFactory {

    public static <T> T selectSingleResult(String sql, Class<T> tClass) {
        JdbcTemplate jdbcTemplate = ConnectUtils.getJdbcTemplate();
        List<T> tList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(tClass));
        return CollectionUtil.isNotEmpty(tList) ? tList.get(0) : null;
    }

    public static <T> List<T> selectResult(String sql, Class<T> tClass) {
        JdbcTemplate jdbcTemplate = ConnectUtils.getJdbcTemplate();
        // 获取主表主键名称
        String primaryKey = getPrimaryKey(tClass);
        String masterPrimaryKey = primaryKey;
        // 获取主表数据
        List<T> masterList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(tClass));
        // 将主表数据转换成 JSONArray
        JSONArray array = JSONArray.parseArray(JSONArray.toJSONString(masterList));
        // 检查是否存在一对一模型关联, 有的话就进行填充, 没有就直接返回主表即可
        List<RelationParams> aList = getAssociationFileName(tClass);
        if (!CollectionUtils.isEmpty(aList)) {
            for (int i = 0; i < array.size(); i++) {
                // 获取主键的值
                String masterPrimaryKeyValue = String.valueOf(array.getJSONObject(i).get(masterPrimaryKey));
                // 关联模型查询
                for (RelationParams relate : aList) {
                    if (Objects.equals(ASSOCIATION.getValue(), relate.getRelationType())) {
                        // 根据类型查询
                        String columnType = relate.getColumnType();
                        try {
                            // 获取到类
                            Class<?> aClass = Class.forName(columnType);
                            // 根据类获取数据库表名
                            String relationTableName = getTableName(aClass);
                            // 获取关联表主键
                            String relationPrimaryKey = getPrimaryKey(aClass);
                            // 查询语句
                            String R_SQL = SELECT_PREFIX
                                    .concat(relationTableName)
                                    .concat(SEPARATOR_WHERE)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_EQUAL)
                                    .concat(SEPARATOR_OPEN)
                                    .concat(SEPARATOR_SELECT)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_FROM)
                                    .concat(relationTableName)
                                    .concat(SEPARATOR_WHERE)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_EQUAL)
                                    .concat(masterPrimaryKeyValue)
                                    .concat(SEPARATOR_CLOSE);
                            TraceLog.out(R_SQL);
                            // 进行查询
                            List<?> relationList = jdbcTemplate.query(R_SQL, new BeanPropertyRowMapper<>(aClass));
                            if (!CollectionUtils.isEmpty(relationList)) {
                                array.getJSONObject(i).put(relate.getColumnName(), relationList.get(0));
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        // 获取这个实体类中有多少个一对多映射
        List<RelationParams> collectionMappingList = ORMUtils.getCollectionFileName(tClass);
        if (!CollectionUtils.isEmpty(collectionMappingList)) {
            // 一个一个映射的来
            for (RelationParams collection : collectionMappingList) {
                // 遍历中间表映射, 双主键数据表
                List<MiddleParams> mList = getMiddleMappingParams(collection.getRelationClass());
                // 获取一的一方主表的类型
                Class<?> masterClass = collection.getMasterClass();
                // 用来判断哪个是目标表(多的一方)
                boolean isTheSameMaster = mList.get(0).getMappingClass().equals(masterClass);
                if (!CollectionUtils.isEmpty(mList)) {
                    for (int i = 0; i < masterList.size(); i++) {
                        // 获取主键的值
                        String masterPrimaryKeyValue = String.valueOf(array.getJSONObject(i).get(masterPrimaryKey));
                        // 获取目标映射表(多的一方)的主键名称
                        String secondMappingPrimaryParam = getPrimaryKey(mList.get(isTheSameMaster ? 1 : 0).getMappingClass());
                        String secondPrimaryKey = secondMappingPrimaryParam;
                        // 获取中间表类型
                        String M_SQL = SELECT_PREFIX
                                .concat(mList.get(isTheSameMaster ? 1 : 0).getMappingTableName())
                                .concat(SEPARATOR_WHERE)
                                .concat(secondPrimaryKey)
                                .concat(SEPARATOR_IN)
                                .concat(SEPARATOR_OPEN)
                                .concat(SEPARATOR_SELECT)
                                .concat(mList.get(isTheSameMaster ? 1 : 0).getMappingColumn())
                                .concat(SEPARATOR_FROM)
                                .concat(mList.get(isTheSameMaster ? 0 : 1).getTableName())
                                .concat(SEPARATOR_WHERE)
                                .concat(mList.get(isTheSameMaster ? 0 : 1).getMappingColumn())
                                .concat(SEPARATOR_EQUAL)
                                .concat(masterPrimaryKeyValue)
                                .concat(SEPARATOR_CLOSE);
                        TraceLog.out(M_SQL);
                        // 进行查询
                        List<?> moreList = jdbcTemplate.query(M_SQL, new BeanPropertyRowMapper<>(mList.get(isTheSameMaster ? 1 : 0).getMappingClass()));
                        if (!CollectionUtils.isEmpty(moreList)) {
                            array.getJSONObject(i).put(collection.getColumnName(), moreList);
                        }
                    }
                }
            }
        }
        return (List<T>) array;
    }

    public static <T> PageResult<T> selectPageResult(String sql, Class<T> tClass) {
        PageResult<T> pageResult = (PageResult<T>) PageResult.builder().build();
        JdbcTemplate jdbcTemplate = ConnectUtils.getJdbcTemplate();
        // 获取主表主键名称
        String primaryKey = getPrimaryKey(tClass);
        // 获取主表数据
        List<T> masterList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(tClass));
        // 将主表数据转换成 JSONArray
        JSONArray array = JSONArray.parseArray(JSONArray.toJSONString(masterList));
        // 检查是否存在一对一模型关联, 有的话就进行填充, 没有就直接返回主表即可
        List<RelationParams> aList = getAssociationFileName(tClass);
        if (!CollectionUtils.isEmpty(aList)) {
            for (int i = 0; i < array.size(); i++) {
                // 获取主键的值
                String masterPrimaryKeyValue = String.valueOf(array.getJSONObject(i).get(primaryKey));
                // 关联模型查询
                for (RelationParams relate : aList) {
                    if (Objects.equals(ASSOCIATION.getValue(), relate.getRelationType())) {
                        // 根据类型查询
                        String columnType = relate.getColumnType();
                        try {
                            // 获取到类
                            Class<?> aClass = Class.forName(columnType);
                            // 根据类获取数据库表名
                            String relationTableName = getTableName(aClass);
                            // 获取关联表主键
                            String relationPrimaryKey = getPrimaryKey(aClass);
                            // 查询语句
                            String R_SQL = SELECT_PREFIX
                                    .concat(relationTableName)
                                    .concat(SEPARATOR_WHERE)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_EQUAL)
                                    .concat(SEPARATOR_OPEN)
                                    .concat(SEPARATOR_SELECT)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_FROM)
                                    .concat(relationTableName)
                                    .concat(SEPARATOR_WHERE)
                                    .concat(relationPrimaryKey)
                                    .concat(SEPARATOR_EQUAL)
                                    .concat(masterPrimaryKeyValue)
                                    .concat(SEPARATOR_CLOSE);
                            TraceLog.out(R_SQL);
                            // 进行查询
                            List<?> relationList = jdbcTemplate.query(R_SQL, new BeanPropertyRowMapper<>(aClass));
                            if (!CollectionUtils.isEmpty(relationList)) {
                                array.getJSONObject(i).put(relate.getColumnName(), relationList.get(0));
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        // 获取这个实体类中有多少个一对多映射
        List<RelationParams> collectionMappingList = ORMUtils.getCollectionFileName(tClass);
        if (!CollectionUtils.isEmpty(collectionMappingList)) {
            // 一个一个映射的来
            for (RelationParams collection : collectionMappingList) {
                // 遍历中间表映射, 双主键数据表
                List<MiddleParams> mList = getMiddleMappingParams(collection.getRelationClass());
                // 获取一的一方主表的类型
                Class<?> masterClass = collection.getMasterClass();
                // 用来判断哪个是目标表(多的一方)
                boolean isTheSameMaster = mList.get(0).getMappingClass().equals(masterClass);
                if (!CollectionUtils.isEmpty(mList)) {
                    for (int i = 0; i < masterList.size(); i++) {
                        // 获取主键的值
                        String masterPrimaryKeyValue = String.valueOf(array.getJSONObject(i).get(primaryKey));
                        // 获取目标映射表(多的一方)的主键名称
                        String secondMappingPrimaryParam = getPrimaryKey(mList.get(isTheSameMaster ? 1 : 0).getMappingClass());
                        String secondPrimaryKey = secondMappingPrimaryParam;
                        // 获取中间表类型
                        String M_SQL = SELECT_PREFIX
                                .concat(mList.get(isTheSameMaster ? 1 : 0).getMappingTableName())
                                .concat(SEPARATOR_WHERE)
                                .concat(secondPrimaryKey)
                                .concat(SEPARATOR_IN)
                                .concat(SEPARATOR_OPEN)
                                .concat(SEPARATOR_SELECT)
                                .concat(mList.get(isTheSameMaster ? 1 : 0).getMappingColumn())
                                .concat(SEPARATOR_FROM)
                                .concat(mList.get(isTheSameMaster ? 0 : 1).getTableName())
                                .concat(SEPARATOR_WHERE)
                                .concat(mList.get(isTheSameMaster ? 0 : 1).getMappingColumn())
                                .concat(SEPARATOR_EQUAL)
                                .concat(masterPrimaryKeyValue)
                                .concat(SEPARATOR_CLOSE);
                        TraceLog.out(M_SQL);
                        // 进行查询
                        List<?> moreList = jdbcTemplate.query(M_SQL, new BeanPropertyRowMapper<>(mList.get(isTheSameMaster ? 1 : 0).getMappingClass()));
                        if (!CollectionUtils.isEmpty(moreList)) {
                            array.getJSONObject(i).put(collection.getColumnName(), moreList);
                        }
                    }
                }
            }
        }
        // (List<T>) array
        pageResult.setRecords((List<T>) array);
        // 查询总记录数
        int fromIndex = sql.indexOf("FROM") + "FROM".length();
        int limitIndex = sql.lastIndexOf("LIMIT");
        String queryCountSql = SELECT_COUNT_PREFIX.concat(sql.substring(fromIndex, limitIndex));
        int count = jdbcTemplate.queryForObject(queryCountSql, Integer.class);
        pageResult.setTotal(count);
        // 当前页
        String tmp = sql.substring(sql.indexOf("LIMIT") + "LIMIT".length());
        String[] split = tmp.split(",");
        String currentPageNum = split[0].trim();
        pageResult.setPageNum(Integer.parseInt(currentPageNum) + 1);
        // 每页展示的总条数
        String pageSize = split[1].trim();
        pageResult.setPageSize(Integer.valueOf(pageSize));
        return pageResult;
    }

    public static <T> void execute(T t, String sql) {
        ConnectUtils.getJdbcTemplate()
                .update(con -> con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS), new GeneratedKeyHolder());
    }

    public static <T> void execute(String sql) {
        ConnectUtils.getJdbcTemplate().update(con -> con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS));
    }

}
